package com.beeasy.hzbpm.ctrl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.beeasy.hzbpm.bean.Doc;
import com.beeasy.hzbpm.bean.MQService;
import com.beeasy.hzbpm.bpm.MQType;
import com.beeasy.hzbpm.entity.BpmData;
import com.beeasy.hzbpm.entity.BpmInstance;
import com.beeasy.hzbpm.filter.Auth;
import com.beeasy.hzbpm.service.BpmService;
import com.beeasy.hzbpm.util.Result;
import com.github.llyb120.nami.core.MultipartFile;
import com.github.llyb120.nami.ctrl.Ctrl;
import com.github.llyb120.json.Arr;
import com.github.llyb120.json.Json;
import com.github.llyb120.json.Obj;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import org.beetl.sql.core.engine.PageQuery;
import org.bson.BsonArray;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

import static com.beeasy.hzbpm.bean.FileStorage.storage;
import static com.beeasy.hzbpm.bean.MongoService.db;
import static com.beeasy.hzbpm.bean.Util.isSu;
import static com.github.llyb120.nami.func.Function.func;
import static com.github.llyb120.json.Json.*;

public class workflow extends Ctrl {


    private MongoCollection<Document> getCollection() {
        return db.getCollection("workflow");
    }

    @Doc
    public Object saveComment(String id, Obj body){
        BpmService service = BpmService.ofIns(id);
        BpmInstance.Comment comment = service.saveComment(Auth.getUid(), body);
        return Result.ok(comment);
    }

    public Object getWorkflowNames2(){
        Arr ret = db.getCollection("cat").aggregate(
            a(
                o("$match", o("type", "1")),
                o("$lookup", o(
                    "from", "workflow",
                    "let", o(
                        "par_id", "$_id"
                    ),
                    "as", "children",
                    "pipeline", a(
                        o(
                            "$match", o(
                                "$expr", o(
                                    "$eq", a("$pid", "$$par_id")
                                )
                            )
                        ),
                        o(
                            "$project", o(
                                "_id", o(
                                    "$toString", "$_id"
                                ),
                                "modelName", 1
                            )
                        )
                    )
                )),
                o(
                    "$project", o(
                        "_id", 0,
                        "children", 1,
                        "name", 1
                    )
                )
            ).toBson()
        ).into(a());
        return Result.ok(ret);
    }

    public Object getWorkflowNames() {
        return Result.ok(getCollection().aggregate(a(
                o("$project",
                    o("modelName", 1,
                        "_id", o(
                            "$toString", "$_id"
                        )
                    )
                )
        ).toBson()).into(a()));
    }

    public Object getWorkflowFormFields(String id){
        Document item = getCollection().aggregate(
            a(
                o("$match", o("_id", new ObjectId(id))),
                o("$project", o("formId", 1))
            ).toBson()
        ).first();;
        if (item == null) {
            return Result.error("找不到对应的流程");
        }
        item = db.getCollection("form").aggregate(
            a(
                o("$match", o("_id", (item.getObjectId("formId")))),
                o("$project", o("data", "$form.data"))
            ).toBson()
        ).first();
        if (item == null) {
            return Result.error("找不到对应的表单");
        }
        return Result.ok(item.get("data"));
    }

    /**
     * 测试用，仅管理员可以调用
     *
     * @param id
     * @return
     */
    public Result backwards(String id, String logId) {
        BpmService service = BpmService.ofIns(id);
        service.backwards(Auth.getUid(), logId);
        return Result.ok();
    }

    /**
     * 回撤
     */
    @Doc
    public Result canToRetreat(String id) {
        BpmService service = BpmService.ofIns(id);
        service.canToRetreat(Auth.getUid());
        return Result.ok();
    }

    /**
     * 同步到最新模型
     *
     * @param id
     * @return
     */
    @Doc
    public Result syncModel(String id) {
        BpmService service = BpmService.ofIns(id);
        service.syncModel(Auth.getUid());
        return Result.ok();
    }

    public MultipartFile jslib() {
        return new MultipartFile("lib.js", "classpath:lib.js");
    }

    /**
     * 保存会签信息
     *
     * @param id
     * @param sign
     * @param over
     * @return
     */
    @Doc
    public Object countersign(String _id, Obj sign, boolean over, String nodeId, String id, String logId) {
        BpmService service = BpmService.ofIns(_id);
        // 并发情况，当前节点为多个时，根据登录人获取对应的nodeId
        nodeId = service.loginCurrentNode(Auth.getUid() + "");
        String msg = service.countersign(Auth.getUid(), sign, over, nodeId, logId);
        return Result.ok(msg);
    }

    /**
     * 检查某个任务是否合法
     *
     * @param id
     * @return
     */
    public Result check(String id) {
        return Result.ok(BpmService.ofModel(id).checkModel());
    }

    @Doc
    public Result upload(MultipartFile file) throws IOException {
        return Result.ok(
                o(
                        "name", file.fileName,
                        "type", file.contentType,
                        "id", storage.upload(file),
                        "creator", Auth.getUid() + "",
                        "action", "upload"
                )
        );
    }

    /**
     * 任务模型树列表
     *
     * @return
     */
    public Object modelList() {
        MongoCollection<Document> collection = db.getCollection("cat");
        Arr cats = collection.aggregate(a(
                o("$match", o("type", "1")),
                o(
                        "$lookup", o(
                                "from", "workflow",
                                "localField", "_id",
                                "foreignField", "pid",
                                "as", "children"
                        )
                ),
                o(
                        "$project", o(
                                "id", o(
                                        "$toString", "$_id"
                                ),
                                "text", "$name",
                                "pid", o(
                                        "$toString", "$pid"
                                ),
                                "type", "cat",
                                "state.opened", "true",
                                "children", o(
                                        "$map", o(
                                                "input", "$children",
                                                "as", "item",
                                                "in", o(
                                                        "id", o("$toString", "$$item._id"),
                                                        "text", "$$item.modelName",
                                                        "type", "form"
                                                )
                                        )
                                )
                        )
                )
        ).toBson()).into(a());
        Arr tree = Json.toTree((List) cats, "pid", "id");
        return Result.ok(tree);
    }

    @Doc
    public Object pause(String id) {
        BpmService service = BpmService.ofIns(id);
        service.pause(Auth.getUid() + "");
        return Result.ok();
    }

    @Doc
    public Object resume(String id) {
        BpmService service = BpmService.ofIns(id);
        service.resume(Auth.getUid() + "");
        return Result.ok();
    }

    @Doc
    public Object forceEnd(String id) {
        BpmService service = BpmService.ofIns(id);
        service.forceEnd(Auth.getUid() + "");
        return Result.ok();
    }

    @Doc
    public Object forceResume(String id) {
        BpmService service = BpmService.ofIns(id);
        service.forceResume(Auth.getUid() + "");
        return Result.ok();
    }

    @Doc
    public Object goBack(String id, Obj back, String logId) {
        BpmService service = BpmService.ofIns(id);
        service.goBack(Auth.getUid(), back, logId);
        return Result.ok();
    }


    public Object uploadImage(String base64) {
        return Result.ok(storage.upload(base64));
    }

    public MultipartFile download(String path, String name) {
        MultipartFile file = storage.download(path);
        if (StrUtil.isNotBlank(name)) {
            file.fileName = name;
        }
        return file;

    }

    /**
     * 并发
     * 可处理节点
     */
    public Object dealCurrent(String id) {
        BpmService service = BpmService.ofIns(id);
        return Result.ok(service.dealCurrent(Auth.getUid() + ""));
    }

    /**
     * 催办
     *
     * @param id
     * @param msg
     * @return
     */
    @Doc
    public Object urge(String id, String msg) {
        BpmService service = BpmService.ofIns(id);
        service.urge(Auth.getUid() + "", msg);
        return Result.ok();
    }

    public Object logList(String id, String nodeId) {
        MongoCollection<Document> collection = db.getCollection("bpmInstance");
        Arr condition = a(
                o(
                        "$match", o(
                                "_id", new ObjectId(id)
                        )

                )
        );
        if (StrUtil.isBlank(nodeId)) {
            condition.add(o(
                    "$project", o("logs", 1)
            ));
        } else {
            condition.add(
                    o(
                            "$project", o(
                                    "logs", o(
                                            "$filter", o(
                                                    "input", "$logs",
                                                    "as", "item",
                                                    "cond", o("$eq", a("$$item.nodeId", nodeId))
                                            )
                                    )
                            )
                    )
            );
        }
        Object logs = ((Map) collection.aggregate(condition.toBson()).first()).get("logs");
        return Result.ok(logs);
//        return Result.ok(((List<Map>) collection.aggregate(condition.toBson()).first().get("logs")));
    }


    @Doc
    public Object copyBpm(String id) {
        MongoCollection<Document> col = db.getCollection("workflow");
        Document doc = col.find(Filters.eq("_id", new ObjectId(id))).first();
        if (doc == null) {
            return Result.error("复制失败");
        }
        doc.put("modelName", doc.getString("modelName") + "副本");
        doc.remove("_id");
        col.insertOne(doc);
        return Result.ok();
    }

    /**
     * 菜单
     *
     * @return
     */
    public Object menu() {
        MongoCollection<Document> collection = db.getCollection("cat");
        Arr cats = collection.aggregate(a(
                o("$match", o("type", "1")),
                o(
                        "$lookup", o(
                                "from", "workflow",
                                "localField", "_id",
                                "foreignField", "pid",
                                "as", "wfs"
                        )
                ),
                o(
                        "$project", o(
                                "_id", o(
                                        "$toString", "$_id"
                                ),
                                "name", 1,
                                "pid", o(
                                        "$toString", "$pid"
                                ),
                                "wfs._id", 1,
                                "wfs.modelName", 1,
                                "wfs.arrangementData.listFields", 1
                        )
                )
        ).toBson()).into(a());
        List ret = (List) toTree((Collection) cats, "pid", "_id");


//        Arr wfs = getCollection().aggregate(a(
//                o("$project",o("_id", o(
//                                        "$toString", "$_id"
//                                ), "modelName",1,
//                            "listFields", "$arrangementData.listFields"
//                        ))
//        ).toBson()).into(a());

//        MongoCollection<Document> catcol = db.getCollection("cat");
        for (Object o : ret) {
            walkCats((Map) o);
        }
        Result ok = Result.ok(ret);
        return ok;
    }

    private void walkCats(Map cat) {
        List children = new ArrayList();
        //扔进去工作流
        List wfs = (List) cat.get("wfs");
        children.addAll(wfs);
        for (Object o : wfs) {
            Map map = (Map) o;
            map.put("_id", ((ObjectId) map.get("_id")).toString());
            map.put("name", map.get("modelName"));
            Map ar = (Map) map.get("arrangementData");
            map.put("href", "./htmlsrc/bpm/ins/ins.list.html?id=" + map.get("_id") + "&fields=" + URLUtil.encode(Json.stringify(ar.get("listFields"))));
        }
        List _children = (List) cat.get("children");
        if (_children != null) {
            children.addAll(_children);
            for (Object child : _children) {
                walkCats((Map) child);
            }
        }
        if (!children.isEmpty()) {
            cat.put("children", children);
        } else {
            cat.remove("children");
        }
        cat.remove("wfs");

    }


//    public Object preparePub(String id, Obj args) {
//        BpmService service = BpmService.ofModel(id);
//        return Result.ok(service.preparePub(Auth.getUid() + "", args));
//    }

    @Doc
    public Result getInsInfo(String id, Obj args) {
        BpmService service = BpmService.ofIns(id);
        return Result.ok(service.getInsInfo(Auth.getUid() + "", args));
    }


    /**
     * 创建任务实例
     *
     * @param id
     * @return
     */
//    public Object createIns(String id, Obj data, Arr files, Obj sign) {
//        BpmService service = BpmService.ofModel(id);
//        BpmInstance ins = service.createBpmInstance(Auth.getUid() + "", data, files, sign, o());
//        return Result.ok(ins._id.toString());
//    }

    /**
     * 给MQ用的
     * @param id
     * @param ext
     * @return
     */
    public BpmInstance createInsNewToMQ(String id, Obj ext){
        BpmService service = BpmService.ofModel(id);
        BpmInstance ins = service.createBpmInstance(Auth.getUid() + "", ext);//, data, files, sign, o());
        return ins;
    }

    @Doc
    public Result createInsNew(String id, Obj ext){
        BpmService service = BpmService.ofModel(id);
        BpmInstance ins = service.createBpmInstance(Auth.getUid() + "", ext);//, data, files, sign, o());
        return Result.ok(ins._id.toString());
    }

    @Doc
    public Object createChildIns(String insId, String modelId, String logId){
        BpmService service = BpmService.ofIns(insId);
        BpmInstance.CurrentNode cNode = service.loginCurrentNode(Auth.getUid(), logId, false, true);
        service.createChildProcesses(Auth.getUid(), cNode, modelId, true);//, data, files, sign, o());
        return Result.ok();
    }


    /**
     * 得到下一节点的可执行人
     *
     * @param id
     * @return
     */
    public Object getNextDealers(String id, Obj data, String logId) {
        BpmService service = BpmService.ofIns(id);

        // 并发情况，当前节点为多个时，根据登录人获取对应的nodeId
        BpmInstance.CurrentNode cNode = service.loginCurrentNode(Auth.getUid() , logId, true);
        BpmData.Node node = service.getNode(cNode.nodeId);//service.getCurrentNode(Auth.getUid() + "",nodeId);

        Object object = service.getNextNodePersons(Auth.getUid() , o(), cNode);
//        BpmInstance.CurrentNode currentNode = service.getCurrent(Auth.getUid() + "", nodeId);
        String concurrent= node.flow.concurrent;
        boolean canConCurrent = false;
        boolean forceConCurrent = false;
        boolean allowDynamic = false;
        if(null != concurrent){
            canConCurrent = StrUtil.equals(concurrent,"allow") || StrUtil.equals(concurrent,"force") || StrUtil.equals("allowDynamic", concurrent);
            forceConCurrent = StrUtil.equals(concurrent,"force");
            allowDynamic = !StrUtil.equals("allowDynamic", concurrent);
        }
        return Result.ok(
                o(
                        "node", cNode,
                        "next", object,
                        "ins", o(
                                "id", service.ins.id,
                                "state", service.ins.state,
                                "bpmName", service.ins.bpmName
                        ),
                        "msgTemp", node.flow.msgTemp,
                        "concurrent",canConCurrent,
                        "forceConCurrent",forceConCurrent,
                        "allowDynamic",allowDynamic


                ));
    }


    /**
     * 保存选取的下一步处理人
     *
     * @param id
     * @return
     */
    @Doc
    public Object nextApprover(String id, Obj data, Arr files, Obj body, Obj sign, String logId) {
        BpmService service = BpmService.ofIns(id);
        return Result.ok(service.nextApprover(Auth.getUid() + "", body, data, files, sign, logId));
    }

    @Doc
    public Result editIns(String id, String logId, boolean validate, Obj data, String mode, Arr files, Obj sign){
        return saveIns(id, logId, validate, data, mode, files, sign);
    }

    /**
     * 保存节点数据
     *
     * @param id
     * @param data
     * @return
     */
    @Doc
    public Result saveIns(String id, String logId, boolean validate, Obj data, String mode, Arr files, Obj sign) {
        BpmService service = BpmService.ofIns(id);
        BpmInstance.CurrentNode cNode = null;
        if ("edit".equals(mode)) {
            cNode =service.getCurrentLog(logId);
        }else{
            cNode = service.loginCurrentNode(Auth.getUid(), logId, false, true);
        }
        return Result.ok(service.saveIns(Auth.getUid() + "", data, validate, mode, files, sign, cNode));
    }

    /**
     * 提交节点数据
     *
     * @param id
     * @return
     */
//    @Deprecated
//    public Result subIns(String id, Obj data, Arr files, Obj sign) {
//        BpmService service = BpmService.ofIns(id);
//        Object bl = service.submitIns(Auth.getUid() + "", data, files, sign);
//        Map<Object, Object> res = new HashMap<>();
//        res.put("id", id);
//        res.put("res", bl);
//        return Result.ok(res);
//    }
    public Object addComment(String id, String content) {
        BpmService service = BpmService.ofIns(id);
        service.addComment(Auth.getUid(), content);
        return Result.ok();
    }


    /**
     * 生成最终的流程文件
     */
    public Result saveWorkFlow(Obj body) {

        MongoCollection<Document> collection = db.getCollection("workflow");
        Arr jsonArray = a();
        jsonArray.add(body);
        Document doc = new Document();
        doc.put("createTime", new Date());
        doc.put("data", BsonArray.parse(jsonArray.toString()));
        String workflowName = body.s("workflowName");

        Bson filter = Filters.eq("workflowName", workflowName);
        FindIterable<Document> lists = collection.find(filter);
        MongoCursor<Document> mongoCursor = lists.iterator();

        if (mongoCursor.hasNext()) {
            collection.updateOne(Filters.eq("workflowName", workflowName), new Document("$set", doc), new UpdateOptions().upsert(true));
        } else {
            collection.insertOne(doc);

        }

        return (Result.ok(doc));
    }

    /**
     * 删除流程
     */
    @Doc
    public Object deleteIns(String id) {
        BpmService service = BpmService.ofIns(id);
        service.delete(Auth.getUid() + "");
        return Result.ok();
    }

    @Doc
    public Object deleteInss(Obj ids, Obj data, Obj body) {
        BpmService service = new BpmService();
        service.delete(Auth.getUid() + "", body);
        return Result.ok();
    }

    @Doc
    public Object getInsList(String type, String id, Integer page, Integer size) {
        if (page == null || page < 1) {
            page = 1;
        }
        if (size == null || size < 1) {
            size = 20;
        }
        BpmService util = new BpmService();

        List<String> roles = util.getCurrentRoles(Auth.getUid());

        Arr bid = aaa(util.workflowSet(Auth.getUid()));
        if (StrUtil.isNotBlank(id)) {
            if (bid.contains(new ObjectId(id))) {
                bid = a(new ObjectId(id));
            } else {
                bid = a(new ObjectId());

            }
        }
        if (bid.isEmpty()) {
            bid = a(new ObjectId());
        }

        String uid = Auth.getUid() + "";
        MongoCollection<Document> col = db.getCollection("bpmInstance");
        Obj match = null;

        Obj types = o(
                "todo", a("流转中", "已暂停"),
                "processed", a("流转中", "已办结", "已暂停"),
                "over", a("已办结", "强制结束")
        );
        Date startDate = $request.date("startTime", "yyyy-MM-dd");
        Date endDate = $request.date("endTime", "yyyy-MM-dd");


        // 当前经办人
        List<String> currIds = new ArrayList<>();
        String mainUid = $request.s("currUid", "");
        List<String> mainUids = Arrays.asList(mainUid.split(","));
        if (!mainUid.isEmpty()) {
            currIds.addAll(mainUids);
        }

//        String[] appSearch = {"id", "pubUName", "attrs.起草人"};
//        List appList = Arrays.asList(appSearch);
        // 起草人
        List<String> pubUids = new ArrayList<>();
        String pubUid = $request.s("pubUid", "");
        List<String> pubUidList = Arrays.asList(pubUid.split(","));
        if (!pubUid.isEmpty()) {
            pubUids.addAll(pubUidList);
        }

        List<String> ids = new ArrayList<>();
        String currUid = $request.s("currUid", "");
        List<String> currs = Arrays.asList(currUid.split(","));
        if (!currUid.isEmpty()) {
            ids.addAll(currs);
        }

        if (StrUtil.equals("observe", type)) {  // 观察的任务
            match = o(
                    "id", $request.s("mid", "").isEmpty() ? undefined : o("$regex", $request.s("mid", ""))
                    //                ,"bpmId", o("$in", util.isSu(uid) ? undefined : bid)
                    , "$or", func(() -> {

                        if (!currUid.isEmpty()) {
                            ids.addAll(currs);
                        }
                        if (ids.isEmpty()) {
                            return undefined;
                        }
                        return ids.stream()
                                .map(e -> o("currentNodes.mainUsers." + e, o("$exists", true)))
                                .collect(Collectors.toList());
                    })
                    , "$and", a(func(() -> {
                                if (isSu(uid)) {
                                    return undefined;
                                }
                                return undefined;
                            }), aaa(
                            startDate == null ? undefined : o("createTime", o("$gte", startDate)),
                            endDate == null ? undefined : o("createTime", o("$lte", endDate))
                            )
                            , o(

                                    // 起草人
                                    "$or", aaa(
                                            pubUids.isEmpty() ? undefined : pubUids.stream()
                                                    .map(e -> o("pubUid", e))
                                                    .collect(Collectors.toList())
                                    ).or(undefined)
                            )
                            , o(
                                    "bpmId", o("$in", bid)
                            )
                    ).or(undefined)
            );

        } else {
            match = o(
                    "state", o("$in", types.get(type)),
                    "bpmId", /*o("$in", bid),*/ StrUtil.isNotBlank(id) ? new ObjectId(id) : undefined,
//                "id",$request.s("mid","").isEmpty()? undefined : $request.s("mid"),
                    "id", $request.s("mid", "").isEmpty() ? undefined : o("$regex", $request.s("mid", "")),
                    "$or", func(() -> {
//                    List<String> ids = new ArrayList<>();
//                    String currUid = $request.s("currUid", "");
//                    List<String> currs = Arrays.asList(currUid.split(","));
//                    if(!currUid.isEmpty()){
//                        ids.addAll(currs);
//                    }
                        //APP增加模糊搜索条件
//                        if (!$request.s("appSearch", "").isEmpty()) {
//                            return appList.stream()
//                                    .map(e -> o(e, o("$regex", $request.s("appSearch", ""))))
//                                    .collect(Collectors.toList());
//                        }
                        if (ids.isEmpty()) {
                            return undefined;
                        }
                        return ids.stream()
                                .map(e -> o(/*"currentNodes.mainUsers."*/"todoUser." + e, o("$exists", true)))
                                .collect(Collectors.toList());
                    }),
                    "$and", a(func(() -> {
                                if (isSu(uid)) {
                                    return undefined;
                                }
                                switch (type) {
                                    case "todo":
                                        return o(
                                                "$or", aaa(
                                                        /*o("currentNodes.mainUsers." + uid, o("$exists", true)),
                                                        o("currentNodes.supportUsers." + uid, o("$exists", true))*/
                                                        o("todoUser." + uid, o("$exists", true))
//                                                        , o("logs.backup.mainUsers." + uid, o("$exists", true)),
//                                                        o("logs.backup.supportUsers." + uid, o("$exists", true))
                                                )
                                        );

                                    case "processed":
                                        return o("$or", aaa(
                                                /*o("logs.uid", uid),
                                                o("currentNodes.mainUsers." + uid, o("$exists", true)),
                                                o("logs.backup.mainUsers." + uid, o("$exists", true)),
                                                o("logs.backup.supportUsers." + uid, o("$exists", true))*/
                                                o("overUser." + uid, o("$exists", true))
                                        ));

                                    case "over":
//                            return o("logs.uid", uid);
                                        return o("$or", aaa(
                                                o("logs.uid", uid)
                                                , o("logs.backup.mainUsers." + uid, o("$exists", true)), o("logs.backup.supportUsers." + uid, o("$exists", true))
                                        ));

                                }
                                return undefined;
                            }),
                            $expand, aaa(
                                    startDate == null ? undefined : o("createTime", o("$gte", startDate)),
                                    endDate == null ? undefined : o("createTime", o("$lte", endDate))
                            )
                            ,
                            o(
                                    "$or"
                                    // 起草人
                                    , aaa(
                                            pubUids.isEmpty() ? undefined : pubUids.stream()
                                                    .map(e -> o("pubUid", e))
                                                    .collect(Collectors.toList())
                                    ).or(undefined)
                            ).or(undefined)


//                    ,util.isSu(uid) ? undefined : o(
//                            "bpmId",o("$in",aaa(
//                                    bidList.contains(id) ? undefined : bidList)
//                            )
//                    )


//                    aaa(
//                            o(
//                                    "bpmId",o("$in",aaa(
//                                            bidList.contains(id) ? undefined : bidList)
//                                    )
//                            )
//
//                    )

                    ).or(undefined)
            );
        }

//        if(null != $request.s("isWork")){
//        match = a(
//            Filters.in()
//        );
        //---
           /* match = o(
                "state", o("$in", types.get(type)),
//                "bpmId", o("$in", bid), //StrUtil.isNotBlank(id) ? new ObjectId(id) : undefined,
                "id",$request.s("mid","").isEmpty()? undefined : $request.s("mid"),
                "$or", func(() -> {
                    List<String> ids = new ArrayList<>();
                    String currUid = $request.s("currUid", "");
                    List<String> currs = Arrays.asList(currUid.split(","));
                    if(!currUid.isEmpty()){
                        ids.addAll(currs);
                    }
                    if(ids.isEmpty()){
                        return undefined;
                    }
                    return ids.stream()
                        .map(e -> o("currentNodes.mainUsers." + e, o("$exists", true)))
                        .collect(Collectors.toList());
                }),
                "$and", a(func(() -> {
                    if (isSu(uid)) {
                        return undefined;
                    }
                    switch (type) {
                        case "todo":
                            return o(
                                    "$or", aaa(
                                        o("currentNodes.mainUsers." + uid, o("$exists", true)),
                                        o("currentNodes.supportUsers." + uid, o("$exists", true))
                                    )
                                );

                        case "processed":
                            return aaa(
                                o("logs.uid", uid),
                                o("currentNodes.mainUsers." + uid, o("$exists", false))
                            );

                        case "over":
                            return o("logs.uid", uid);

                    }
                    return undefined;
                }),
                   aaa(
                       startDate == null ? undefined : o("createTime", o("$gte",startDate)),
                       endDate == null ? undefined : o("createTime", o("$lte",endDate))
                   )
                    // 起草人
                    ,aaa(
                            pubUids.isEmpty() ? undefined :pubUids.stream()
                                    .map(e -> o("pubUid",e))
                                    .collect(Collectors.toList())
                    )
//                    ,util.isSu(uid) ? undefined : o(
//                            "bpmId",o("$in",aaa(
//                                    bidList.contains(id) ? undefined : bidList)
//                            )
//                    )


//                    aaa(
//                            o(
//                                    "bpmId",o("$in",aaa(
//                                            bidList.contains(id) ? undefined : bidList)
//                                    )
//                            )
//
//                    )

                ).or(undefined)
            );*/
        //--

//        }
        /*else{
            match = o(
                    "state", o("$in", types.get(type)),
    //                "bpmId",o("$in",bidList),
                    "bpmId", StrUtil.isNotBlank(id) ? new ObjectId(id) : undefined,
                    "id",$request.s("mid","").isEmpty()? undefined : $request.s("mid"),
    //            "pubUid",o("$in",$request.s("pubUid","").isEmpty()? undefined : Arrays.asList($request.s("pubUid").split(","))),
                    "$or", func(() -> {
                        List<String> ids = new ArrayList<>();
                        String currUid = $request.s("currUid", "");
                        List<String> currs = Arrays.asList(currUid.split(","));
                        if(!currUid.isEmpty()){
                            ids.addAll(currs);
                        }
                        if(ids.isEmpty()){
                            return undefined;
                        }
                        return ids.stream()
                                .map(e -> o("currentNodes.mainUsers." + e, o("$exists", true)))
                                .collect(Collectors.toList());
                    }),
//                    "$or", a(func(() -> {
//
//
//                                if (util.isSu(uid)) {
//                                    return undefined;
//                                }
//                                switch (type) {
//                                    case "todo":
//                                        return o(
//                                                "$or", aaa(
//                                                        o("currentNodes.mainUsers." + uid, o("$exists", true)),
//                                                        o("currentNodes.supportUsers." + uid, o("$exists", true))
//                                                )
//                                        );
//
//                                    case "processed":
//                                        return aaa(
//                                                o("logs.uid", uid),
//                                                o("currentNodes.mainUsers." + uid, o("$exists", false))
//                                        );
//
//                                    case "over":
//                                        return o("logs.uid", uid);
//
//                                }
//                                return undefined;
//
//                            })
//                            ,util.isSu(uid) ? undefined : o(
//                                            "bpmId",o("$in",aaa(
//                                                    bidList.contains(id) ? undefined : bidList)
//                                            )
//                                    )
//                            // 经办人
//                            ,aaa(
//
//                                    currIds.isEmpty() ? undefined :currIds.stream()
//                                    .map(e -> o("currentNodes.mainUsers." + e, o("$exists", true)))
//                                    .collect(Collectors.toList())
//                            )
//                            // 起草人
//                            ,aaa(
//                                    pubUids.isEmpty() ? undefined :pubUids.stream()
//                                            .map(e -> o("pubUid",e))
//                                            .collect(Collectors.toList())
//                            )
//
//                    ).or(undefined),
                    "$and", a(func(() -> {


                                if (util.isSu(uid)) {
                                    return undefined;
                                }
                                switch (type) {
                                    case "todo":
                                        return o(
                                                "$or", aaa(
                                                        o("currentNodes.mainUsers." + uid, o("$exists", true)),
                                                        o("currentNodes.supportUsers." + uid, o("$exists", true))
                                                )
                                        );

                                    case "processed":
                                        return aaa(
                                                o("logs.uid", uid),
                                                o("currentNodes.mainUsers." + uid, o("$exists", false))
                                        );

                                    case "over":
                                        return o("logs.uid", uid);

                                }
                                return undefined;
                            }),
                            aaa(
                                    startDate == null ? undefined : o("createTime", o("$gte",startDate)),
                                    endDate == null ? undefined : o("createTime", o("$lte",endDate))
                            )
                            ,util.isSu(uid) ? undefined : o(
                                    "bpmId",o("$in",aaa(
                                            bidList.contains(id) ? undefined : bidList)
                                    )
                            )
                            // 经办人
                            ,aaa(

                                    currIds.isEmpty() ? undefined :currIds.stream()
                                            .map(e -> o("currentNodes.mainUsers." + e, o("$exists", true)))
                                            .collect(Collectors.toList())
                            )
                            // 起草人
                            ,aaa(
                                    pubUids.isEmpty() ? undefined :pubUids.stream()
                                            .map(e -> o("pubUid",e))
                                            .collect(Collectors.toList())
                            )
//
//                                    return undefined;
//                            }),
//                            aaa(
//                                    startDate == null ? undefined : o("createTime", o("$gte",startDate)),
//                                    endDate == null ? undefined : o("createTime", o("$lte",endDate))
//                            )

                    ).or(undefined)
            );
        }*/

//        Arr condition = a(
        //我参与的是最优先，所有的检索都要在这里面进行

//        );
//        List<Obj> set = (List) util.workflowSet1(uid);
        int count = (int) col.countDocuments(match.toBson());
        List list = (List) col.aggregate(a(
                o("$match", match),
                o("$project", o(
                        "_id", o("$toString", "$_id"),
                        "id", 1,
                        "attrs", 1,
                        "pubUid", 1,
                        "pubUName", 1,
                        "createTime", 1,
                        "bpmName", 1,
                        "bpmId", 1,
                        "lastModifyTime", 1,
                        "currentNodes", 1,
                        "bpmModel.listFields", 1,
                        "state", 1,
                        "logs", 1,
                        "parInsId", o(
                           "$toString", "$parInsId"
                    )
                )),
                o("$sort", o("lastModifyTime", -1)),
                o("$skip", (page - 1) * size),
                o("$limit", size)
        ).toBson()).into(a())
                .stream()
                .map(e -> {
                    Document doc = (Document) e;
                    Obj obj = o();
//                List<String> perSet = util.funcSet(doc.get("bpmId").toString(), uid, set);

                /*List<Obj> set = (List) util.workflowSet1(uid);
//                set.stream().filter(ee -> ee.size()>0).collect(Collectors.toList());
                List<String> perSet = new ArrayList<>();
                for(Obj li : set){
                    Map<String, Object> maps = li;
                    for(Map.Entry<String ,Object> map : maps.entrySet()){
                        if(StrUtil.equals(map.getKey(),doc.get("bpmId").toString())){
                            System.out.println(map);
                            perSet.addAll((Collection<? extends String>) map.getValue());
                        }
                    }
                }*/
                    obj.putAll(doc);
                    obj.putAll((Map) doc.get("attrs"));
                    obj.remove("attrs");
                    String bpmName = (String) obj.get("bpmName");


                    if(null != bpmName){
                        String[] name = bpmName.split("\\(");
                        if(name != null && name.length>0){
                            obj.put("bpmName",name[0]);
                        }
                    }

                    BpmService bpmService = BpmService.ofIns(doc);

                    Document modelSet = bpmService.getModelSet(bpmService.modelId);

                    Set<String> havaSet = new HashSet<>();
                    if(null == modelSet){
                        return obj;
                    }
                    for(String s : roles){
                        List<String> sets = (List<String>) modelSet.get(s);
                        if(null != sets){
                            for(String set : sets){
                                havaSet.add(set);
                            }
                        }
                    }


                    obj.put("deal", bpmService.canDealCurrent(uid));
                    obj.put("edit", bpmService.canEdit(uid, havaSet));
//                obj.put("edit", bpmService.canEdit(uid));
                    obj.put("forceEnd", bpmService.canForceEnd(uid, havaSet));
                    obj.put("forceResume", bpmService.canForceResume(uid, havaSet));
                    obj.put("pause", bpmService.canPause(uid, havaSet));
                    obj.put("resume", bpmService.canResume(uid, havaSet));
                    obj.put("urge", bpmService.canUrge(uid, havaSet));
                    obj.put("del", bpmService.canDelete(uid, havaSet));
                    obj.put("sync", bpmService.canSync(uid, havaSet));

                    String names = bpmService.ins.currentNodes.stream()
                            .filter(ee -> ee.mainUsers.size() > 0)
                            .flatMap(ee -> ee.mainUsers.values().stream())
                            .map(ee -> String.valueOf(ee))
                            .collect(Collectors.joining(","));
                    obj.put("uName", names);

                    return obj;
                })
                .collect(Collectors.toList());
        PageQuery pq = new PageQuery();
        pq.setPageNumber(page);
        pq.setPageSize(size);
        List newList = new ArrayList();
        for (int i = 0; i < list.size(); i++) {
            Obj obj = (Obj) list.get(i);

            String beforeNode = "";
            List<Document> currentNodes = (List<Document>) obj.get("currentNodes");
            for (int i1 = 0; i1 < currentNodes.size(); i1++) {
                try {
                    Document oNode = currentNodes.get(i1);
                    obj.put("jieshouDate", oNode.get("minTimeout"));
                    beforeNode += oNode.get("nodeName") + ",";
                } catch (Exception e) {
                    obj.put("jieshouDate", "");
                }
            }
            obj.put("beforeNode", beforeNode.length() > 0 ? beforeNode.substring(0, beforeNode.length() - 1) : "");
//            String nodeId = Obj.fromBson(Arr.fromBson(obj.get("currentNodes")).get(0)).get("nodeId").toString();
//            Arr arr = Arr.fromBson(obj.get("logs"));
//            for (Object or : arr) {
//                Obj ox = Obj.fromBson(or);
//                if (ox.get("type").equals("save") && ox.get("nodeId").equals(nodeId)) {
//                    obj.put("jieshouDate", ox.get("startTime"));
//                    break;
//                }
//            }


            try {
//                SimpleDateFormat sdf = new SimpleDateFormat("EEE MMM dd HH:mm:ss z yyyy",Locale.US);
//                DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Date d1 = (Date) obj.get("createTime");
                if (obj.get("state").equals("已办结")) {
                    Date d2 = (Date) obj.get("lastModifyTime");
//                    Date d1 = sdf.parse(obj.get("createTime").toString());
//                    Date d2 = sdf.parse(obj.get("lastModifyTime").toString());
                    obj.put("countTime", getCountTime(d1, d2));
                } else {
//                    Date d1 = sdf.parse(obj.get("createTime").toString());
                    Date d3 = new Date();//df.parse(df.format(new Date()));
                    obj.put("countTime", getCountTime(d1, d3));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            obj.remove("logs");
            obj.remove("currentNodes");
            obj.remove("bpmId");
            newList.add(obj);
        }
        pq.setList(newList);
        pq.setTotalRow(count);
        return Result.ok(pq);
    }

    public String getCountTime(Date d1, Date d2) {
        String countTime = "";
        try {
            long diff = d2.getTime() - d1.getTime();//这样得到的差值是微秒级别
            long days = diff / (1000 * 60 * 60 * 24);
            long hours = (diff - days * (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
            long minutes = (diff - days * (1000 * 60 * 60 * 24) - hours * (1000 * 60 * 60)) / (1000 * 60);
            if (days > 0) {
                countTime += days + "天";
            }
            if (hours > 0) {
                countTime += hours + "小时";
            }
            if (minutes > 0) {
                countTime += minutes + "分钟";
            }
        } catch (Exception e) {
        }
        return countTime;
    }


    // 获取表单字段
    public Object getFields(String id) {

        Obj match = o(
                "_id", !StrUtil.equals("undefined", id) ? new ObjectId(id) : undefined);
        MongoCollection<Document> collection = db.getCollection("workflow");
        Arr workflows = collection.aggregate(a(
                o("$match", match),
                o(
                        "$project", o(
                                "_id", o(
                                        "$toString", "$_id"
                                ),
                                "bpmData.listFields", 1
                        )
                )
        ).toBson()).into(a());

        return Result.ok(workflows);
    }


    // App获取表单字段
    public Object appGetFields() {
        Arr appwordk = new Arr();
        try {
            Arr a = (Arr) $request.a("id");
            for (int i = 0; i < a.size(); i++) {
                MongoCollection<Document> collection = db.getCollection("workflow");
                appwordk.add(collection.aggregate(a(
                        o("$match", o("_id", new ObjectId(a.s(i)))),
                        o(
                                "$project", o(
                                        "_id", o(
                                                "$toString", "$_id"
                                        ),
                                        "bpmData.listFields", 1
                                )
                        )
                ).toBson()).into(a()));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.ok(appwordk);
    }

    /***********************************/

    public Object workflowSet() {
        BpmService bpmService = new BpmService();
//        List arr = bpmService.workflowSet(Auth.getUid());
        Object arr = bpmService.workflowSet1(Auth.getUid());
        return Result.ok(arr);
    }

    public Object getLogNodes(String id) {
        MongoCollection<Document> collection = db.getCollection("bpmInstance");
        Set workflows = collection.aggregate(a(
                o("$match", o("_id", new ObjectId(id))),
                o(
                        "$project", o(
                                "logs", 1
                        )
                )
        ).toBson()).into(a()).stream().map(e -> {
            Document doc = (Document) e;
            List<Obj> li = new ArrayList<>();


//            List<BpmInstance.DataLog> logs = (List<BpmInstance.DataLog>) doc.get("logs");

            List<Document> logs2 = (List<Document>) doc.get("logs");

            for (Document logs : logs2) {
                if (!StrUtil.equals(logs.get("type").toString(), "save")) {
                    Obj obj = o();
                    obj.put("nodeId", logs.get("nodeId"));

                    obj.put("nodeName", logs.get("nodeName") == null ? "" : logs.get("nodeName"));
                    li.add(obj);
                }
            }

//            for (BpmInstance.DataLog list : logs) {
//                if(StrUtil.equals(list.type,"save")){
//                    obj.put("nodeId",list.nodeId);
//                    obj.put("nodeName",list.nodeName);
//                }
//            }

            return li;
        }).collect(Collectors.toSet());

        return Result.ok(workflows);
    }

    /**
     *  批量运行 已办理流程
     * @return
     */
    public Result doOver(){
        MongoCollection<Document> collection = db.getCollection("bpmInstance");
        Collection list = collection.aggregate(
                a(
                        o("$project", o(
                                "_id", o("$toString", "$_id"),
                                "currentNodes.nodeId",1
                        ))
                ).toBson()
        ).into(new ArrayList());
        for (Object o : list) {
            Document doc = (Document) o;
            String id = (String) doc.get("_id");
            List currentNodes = (List) doc.get("currentNodes");
            Obj obj = o();
            BpmService service = BpmService.ofIns(id);
            if(null == service.ins.overUser){
                service.ins.overUser = o();
            }
            for (Object currentNode : currentNodes) {
                Document currDoc = (Document) currentNode;
                String nodeId = (String) currDoc.get("nodeId");
                Obj res = service.overUserAddAll(nodeId);
                obj.putAll(res);
            }
            Obj set = o("overUser",obj);
            if(obj.size()>0){
                UpdateResult res = collection.updateOne(
                        Filters.eq("_id", new ObjectId(id)),
                        o("$set", set).toBson()
                );
            }
        }

        return Result.ok();
    }

    /**
     *  批量运行 待办理流程
     * @return
     */
    public Result doTodo(){
        MongoCollection<Document> collection = db.getCollection("bpmInstance");
        Collection list = collection.aggregate(
                a(
                        o("$project", o(
                                "_id", o("$toString", "$_id"),
                                "currentNodes.mainUsers",1,
                                "currentNodes.supportUsers",1
                        ))
                ).toBson()
        ).into(new ArrayList());
        for (Object o : list) {
            Document doc = (Document) o;
            String id = (String) doc.get("_id");
            List currentNodes = (List) doc.get("currentNodes");
            Obj obj = o();
            BpmService service = BpmService.ofIns(id);
            if(null == service.ins.todoUser){
                service.ins.todoUser = o();
            }
            for (Object currentNode : currentNodes) {
                Document currDoc = (Document) currentNode;
                Document mainUsers = (Document) currDoc.get("mainUsers");
                Document supportUsers = (Document) currDoc.get("supportUsers");
                if(null != mainUsers && !mainUsers.isEmpty()){
                    obj.putAll(mainUsers);
                }
                if(null != supportUsers && !supportUsers.isEmpty()){
                    obj.putAll(supportUsers);
                }
            }
            Obj set = o("todoUser",obj);
            if(obj.size()>0){
                UpdateResult res = collection.updateOne(
                        Filters.eq("_id", new ObjectId(id)),
                        o("$set", set).toBson()
                );
            }
        }

        return Result.ok();
    }


    public Result sendmq(){
        MQService.instance.sendMessage(MQType.TOPIC, $request.ss("topic"), $request.ss("content"));
        return Result.ok();
    }

}
